Перед тем как непосредственно перейти к теме статьи, кратко напомню, что такое шейдеры. Шейдеры, это программы, которые в отличии от обычных программ, выполняются на графическом процессоре видеокарты. Т.е. шейдеры предназначены для замены фиксированной последовательности выполнения некоторых команд по отрисовке графических примитивов. Это придаёт графическим приложениям определённую гибкость и облегчает для программиста реализацию различных видеоэффектов. Первоначально шейдеры писались на ассемблероподобном языке низкого уровня, что было не совсем удобно для программистов. Поэтому советом по пересмотру архитектуры OpenGL (OpenGL Arсhitecture Review Board или сокращённо ARB), куда входят такие известные фирмы как 3Dlabs, Apple, ATI, Dell, Evans & Sutherland, Hewlett-Packard, IBM, Intel, Matrox, NVIDIA, SGI и Sun Microsystems был разработан высокоуровневый язык для написания шейдеров, который назвали GLSL (OpenGL Shading Language). Официально GLSL присутствует в OpenGL версии 2.0, а неофициально, в виде ARB-расширения в OpenGL версии 1.5. GLSL основан на синтаксисе языка С. Конечно имеются некоторые отличия между синтаксисом GLSL и С, но они незначительны и указаны в спецификации по GLSL. Рассмотрим, как в вашей OpenGL программе инициализировать шейдеры. Для инициализации GLSL-шейдеров необходимо выполнить следующую последовательность действий: 1. Проверить поддержку драйвером OpenGL расширений GL_ARB_shader_objects и GL_ARB_shading_language_100 (поддержка GLSL верисии 1.0). 2. Проверить поддержку драйвером OpenGL функций-расширений, необходимых для работы с GLSL-шейдерами. 3. Создать пустые объекты-шейдеры с помощью glCreateShaderObjectARB(). Их может быть один, а может быть и более. Всё зависит от потребностей вашей программы. 4. Считать исходный текст шейдеров из файла (если он содержится во внешнем файле) и при помощи функции glShaderSourceARB() поместить в них исходный код. 5. Выполнить компиляцию каждого шейдера с помощью glCompileShaderARB(). 6. При помощи функции glCreateProgramObjectARB() создать объект-программу. 7. Присоединить все объекты-шейдеры к объекту-программе с помощью glAttachObjectARB(). 8. Скомпоновать объект-программу командой glLinkProgramARB(). 9. Командой glUseProgramObjectARB() установить исполняемую программу в качестве текущей для OpenGL. А сейчас более подробно рассмотрим приведённую выше последовательность действий на примере функций, которые реализованы в классе MyShader. Перед использованием шейдеров необходимо проверить поддерживает ли драйвер видеокарты GLSL. Это можно сделать используя функцию bool IsGLSLSupported() класса MyShader:
Также в функции bool IsGLSLSupported() класса MyShader осуществляется проверка инициализации функций-расширений, предназначенных для работы с шейдерами. В случае успешной инициализации переменная будет содержать адрес соответствующей функции, отличный от нуля. Инициализация этих функций реализована в другом файле и должна быть выполнена до проверки успешности их инициализации. Более подробно механизм работы с расширениями будет описан в другой статье. Эту проверку можно не делать, поскольку если драйвер видеокарты поддерживает GLSL, то функции-расширения для работы с шейдерами будут поддерживаться практически со 100% вероятностью. Здесь я все-таки приведу код инициализации расширений для работы с GLSL-шейдерами. Функция InitGLSL() не входит в класс MyShader.
Следующим шагом будет загрузка исходного текста шейдера в буфер памяти. Предполагается, что исходный текст шейдера хранится в текстовом файле. Для чтения данных из текстового файла служит функция char * LoadFromFile(char * shader_name) из класса MyShader. В качестве входного параметра функция принимает имя текстового файла и после выполнения возвращает указатель на область памяти, в которой хранится содержимое прочитанного файла - текст шейдера.
Шейдерную программу OpenGL хранит в виде объекта, который содержит не только саму программу но и внутренние переменные, которые описывают состояние программы. Поэтому необходимо создать пустой объект-шейдер при помощи функции GLhandleARB glCreateShaderObjectARB(GLenum shaderType)
После выполнения функция возвращает ненулевой заголовок созданного объекта-шейдера, которое в дальнейшем будет использоваться для последующей идентификации этого объекта во многих функциях. Далее необходимо поместить исходный код в пустой объект-шейдер используя функцию void glShaderSourceARB(GLhandleARB shader, GLsizei nstrings, const GLcharARB **strings, const GLint *lengths).
Практический пример использования: glShaderSourceARB(vsh_object, 1, &vsh_data, NULL); Этот код поместил в пустой вершинный объект-шейдер код шейдера, прочитанный из файла.
Следующим шагом является компиляция каждого объекта-шейдера при помощи функции void glCompileShaderARB(GLhandleARB shader).
Практический пример использования: glCompileShaderARB(vsh_object); Этот код компилирует вершинный объект-шейдер.
После компиляции всех объектов-шейдеров, необходимо создать пустую объект- программу, которая будет содержать в себе объекты-шейдеры. Для создания пустого объекта-программы служит функция GLhandleARB glCreateProgramObjectARB(void).
Функция не имеет входных параметров и возвращает уникальный заголовок программы.
После создания пустой объекта-программы, необходимо присоединить к ней откомпилированные объекты-шейдеры используя функцию void glAttachObjectARB(GLhandleARB program, GLhandleARB shader).
Практический пример использования: glAttachObjectARB(sh_program_object, vsh_object); glAttachObjectARB(sh_program_object, fsh_object); Этот код присоединяет к пустой шейдерной объекту-программе вершинный и фрагментный объекты-шейдеры.
Осталось теперь выполнить компоновку объекта-программы при помощи функции void glLinkProgramARB(GLhandleARB program).
Практический пример использования: glLinkProgramARB(sh_program_object); Этот код выполняет компоновку шейдерного объекта-программы с заголовком sh_program_object
Вот и всё!!! Теперь перед рисованием наших объектов необходимо установить исполняемую программу в качестве текущей для контекста рендеринга OpenGL при помощи функции void glUseProgramObjectARB(GLhandleARB program).
Практический пример использования: glUseProgramObjectARB(sh_program_object); Этот код устанавливает шейдерный объект-программу с заголовком sh_program_object текущей дя контекста рендеринга OpenGL. После окончания рисования необходимо запретить выполнение шейдерного объекта-программы вызвав функцию glUseProgramObjectARB(0).
Примечание 1: Перед завершеним программы необходимо отсоединить все объекты-шейдеры от шейдерного объекта-программы при помощи функции void glDetachObjectARB (GLhandleARB program, GLhandleARB shader).
После этого необходимо удалить отсоединённые от объекта-программы объекты-шейдеры и после этого удалить собственно сам шейдерный объект-программу при помощи функции void glDeleteObjectARB(GLhandleARB object).
Функция glDeleteObjectARB() освобожадает область памяти, выделенную под объект и делает недействительным заголовок объекта. Практический пример использования: if(vsh_object) { glDetachObjectARB(sh_program_object, vsh_object); glDeleteObjectARB(vsh_object); vsh_object = NULL; }Этот код демонстрирует как отсоединяется вершинный объект-шейдер от объекта-программы и как производится удаление вершинного объекта-шейдера.
Примечание 2: существует функция, которая сообщает о том, как выполнялись операции по компиляции, присоединению, компоновке, удалению шейдеров. Иначе говоря, эта функция даёт информацию о статусе объекта (шейдера, программы) после выполнения некоторых действий над этим объектом. Это функция void glGetObjectParameterf(i)vARB (GLhandleARB object,GLenum pname, GLfloat (GLint) *params).
Практический пример использования: GLint status = GL_FALSE; glGetObjectParameterivARB(fsh_object, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (status== GL_FALSE) { MessageBox(NULL,"Fragment Shader not compiled", "ERROR",MB_OK|MB_ICONEXCLAMATION); }Этот код проверяет как произошла компиляция фрагментного объекта-шейдера. Если переменная status равна GL_TRUE, то компиляция прошла успешно. Исходный код класса для работы с GLSL-шейдерами можно загрузить здесь. В качестве примера будет использоваться шейдер, выполняющий преобразование цветной RGB-картинки в картинку с оттенками серого. Исходный код и исполняемый файл примера к статье можно загрузить здесь. В процессе написания последующих статей о работе с шейдерами, класс MyShader будет пополняться новыми функциями. Удачи Вам. |